/**
 *****************************************************************************
 * @file    hal_spi_flash.c
 *
 * @brief    -
 *
 * Copyright (C) RealMega 2019-2020
 *
 *****************************************************************************
*/                              

#include "co_types.h"
#include "_reg_spiflash.h"
#include "hal_spi_flash.h"
#include "hal_irq.h"

#define hal_spi_flash_wait_busy()	while (hwp_spiflash->status.v & SPIFLASH_STATUS_BUSY_STAT)

#define hal_spi_flash_wait_read()	while ((hwp_spiflash->status.v & SPIFLASH_STATUS_RX_FIFO_EMPTY) == 0)


typedef struct
{
    uint8_t         cmd_mode;
    uint8_t         data_mode;
    uint8_t         cont_mode;
    uint16_t        page_size;
}hal_spi_flash_ctrl_t;

hal_spi_flash_ctrl_t g_spi_flsh_ctrl;

bool hal_spiflash_is_txfifo_empty(void)
{
    return (hwp_spiflash->status.v & SPIFLASH_STATUS_TX_FIFO_EMPTY);
}

void hal_spi_flash_disable_read(void)
{
	hal_spi_flash_wait_read();

    if(g_spi_flsh_ctrl.cont_mode)
    {
        hal_spi_flash_wait_busy();
    }
    
    //hal_ahb_cache_disable();

    return ;
}


void hal_spi_flash_enable_read(void)
{
    if(g_spi_flsh_ctrl.cont_mode)
    {
        hal_spi_flash_wait_busy();
    }

    //hal_ahb_cache_enable();
    return ;
}

bool hal_spi_flash_send_data(const uint8_t *data, uint32_t length, bool quard_mode)
{
    uint32_t i;
    uint32_t temp;

	hal_spi_flash_wait_busy();

    for(i = 0; i < length; i++)
    {
        temp = (uint32_t)data[i];
        
        if(quard_mode)
        {
            temp = temp | 0x100;
        }
        
        if(hwp_spiflash->status.v & SPIFLASH_STATUS_TX_FIFO_FULL)
        {
        	return false;
        }
        else
        {
        	hwp_spiflash->tx_fifo_data.v = temp;
        }
    }
    return true;
}

uint32_t hal_spi_flash_recv_data(uint8_t* buf, uint32_t length)
{
    uint32_t i;
    uint32_t nbAvailable = length;

    // Check the address pointer.
    while (hwp_spiflash->status.v & SPIFLASH_STATUS_RX_FIFO_EMPTY);

    hal_spi_flash_wait_busy();
    
    // Get data byte by byte.
    for (i = 0; i < nbAvailable; i++)
    {
        buf[i] = (uint8_t) hwp_spiflash->rx_fifo_data.v;
    }

    return i;
}

bool hal_spi_flash_flush_fifo(bool clr_rx,  bool clr_tx)
{
    uint32_t value = 0;
    uint32_t flag  = 0;

    if(clr_rx)
    {
        value |= SPIFLASH_FIFO_CTRL_RX_FIFO_CLR;
        flag  |= SPIFLASH_STATUS_RX_FIFO_EMPTY;
    }

    if(clr_tx)
    {
        value |= SPIFLASH_FIFO_CTRL_TX_FIFO_CLR;
        flag  |= SPIFLASH_STATUS_TX_FIFO_EMPTY;
    }

    hal_spi_flash_wait_busy();
    hwp_spiflash->fifo_ctrl.v = value;

    return true;
}

void hal_spi_flash_init(uint8_t read_mode, uint8_t cmd_mode, uint8_t clk_div)
{
#if 0
    uint32_t config;
    hal_spi_flash_wait_busy();
    
    hwp_spiflash->flash_config.v = config;
#endif
}

void hal_spi_flash_enable_continuous_mode(bool enable)
{
    hal_spi_flash_wait_busy();

    if(enable)
    {
        hwp_spiflash->trans_mode.v |= SPIFLASH_TRANS_MODE_TX_CONT_MODE;
    }
    else
    {
        hwp_spiflash->trans_mode.v &= ~SPIFLASH_TRANS_MODE_TX_CONT_MODE;
    }
}

void hal_spi_flash_send_command(uint8_t cmd)
{
    //uint32_t cmd_addr;
    hal_spi_flash_wait_busy();

    //cmd_addr = 0xFFFFFF00 | cmd;
    hwp_spiflash->tx_cmd_addr.v = cmd;
}

void hal_spi_flash_erase(uint8_t cmd, uint32_t addr)
{
    hal_spi_flash_wait_busy();
    
    addr = ((addr << 8) & SPIFLASH_TX_CMD_ADDR_TX_ADDR_MASK) | cmd;

    hwp_spiflash->tx_cmd_addr.v = addr;
}

void hal_spi_flash_page_program(uint8_t cmd, uint32_t addr)
{
    hal_spi_flash_wait_busy();
    
    addr = ((addr << 8) & SPIFLASH_TX_CMD_ADDR_TX_ADDR_MASK) | cmd;

    hwp_spiflash->tx_cmd_addr.v = addr;
}

uint8_t hal_spi_flash_read_status_register(uint8_t cmd)
{
    uint8_t  status = 0;
    //uint32_t cmd_addr;
    
    hal_spi_flash_wait_busy();
    hwp_spiflash->trans_mode.b.tx_block_size = 1;
    
    //cmd_addr = SPIFLASH_TX_CMD_ADDR_TX_ADDR_MASK | cmd;
    hal_spi_flash_wait_busy();
    hwp_spiflash->tx_cmd_addr.v = cmd;
    
    hal_spi_flash_recv_data(&status, 1);
    
    return status;
}

typedef struct
{
    uint8_t     manu_id;
    uint8_t     dev_id;
}flash_info_t;

flash_info_t g_flsh_info;


#define hal_spi_flash_wait_busy()	while (hwp_spiflash->status.v & SPIFLASH_STATUS_BUSY_STAT)

#define hal_spi_flash_wait_read()	while ((hwp_spiflash->status.v & SPIFLASH_STATUS_RX_FIFO_EMPTY) == 0)

void hal_spi_flash_read_ids(uint8_t cmd, uint8_t* buf, uint8_t length)
{
    uint32_t i;
    hal_spi_flash_wait_busy();
    hwp_spiflash->trans_mode.b.tx_block_size = length;

    hal_spi_flash_wait_busy();
    hwp_spiflash->tx_cmd_addr.v = cmd;

    hal_spi_flash_wait_busy();

    // Check the address pointer.
    while (hwp_spiflash->status.v & SPIFLASH_STATUS_RX_FIFO_EMPTY);

    // Get data byte by byte.
    for (i = 0; i < length; i++)
    {
        buf[i] = (uint8_t) hwp_spiflash->rx_fifo_data.v;
    }
}


// P25D22L    man_id 0x85, dev_id 0x11
// GD25WD40E  man_id 0xC8, dev_id 0x12
// BY25Q20AW  man_id 0x68, dev_id 0x11
uint8_t drv_flash_enable_write_protect(bool enable)
{
    uint8_t status_w = 0;
    uint8_t status_r = 0;
    
    if(g_flsh_info.manu_id == 0)
    {
        uint8_t buf[2];
        hal_spi_flash_read_ids(FLASH_CMD_REMS, buf, 2);

        g_flsh_info.manu_id = buf[0];
        g_flsh_info.dev_id  = buf[1];
    }

    if((g_flsh_info.manu_id == 0x85) || (g_flsh_info.manu_id == 0xc8) || (g_flsh_info.manu_id == 0x68))
    {
        if(g_flsh_info.dev_id == 0x11 || g_flsh_info.dev_id == 0x12)
        {
            if(enable)
            {
                // BP0 AND BP1 set to 1.
                status_w = (3<<2);
            }
            else
            {
                // BP0 AND BP1 set to 0
                status_w = 0;
            }
        }
    }
    else
    {
        return 1;
    }
    
    HAL_SYS_DISABLE_IRQ();
    hal_spi_flash_disable_read();
    hal_spi_flash_send_command(FLASH_CMD_DISABLE_QPI);

	hal_spi_flash_flush_fifo(false, true);
    hal_spi_flash_send_data(&status_w, 1, 0);

    hal_spi_flash_send_command(FLASH_CMD_WRITE_ENABLE);
    hal_spi_flash_send_command(FLASH_CMD_WRITE_STATUS_REG);

    /*retrieve status register's value of flash chip, S0 was BUSY bit.*/
    while(status_r & 0x01)
    {
        hal_spi_flash_flush_fifo(true, false);
        status_r = hal_spi_flash_read_status_register(FLASH_CMD_READ_STATUS_REG1);
    }

    hal_spi_flash_enable_read();
    HAL_SYS_ENABLE_IRQ();

    if((status_r & status_w) == status_w)
    {
        return CO_ERR_SUCCESS;
    }
    else
    {
        return 2;
    }
}

bool drv_flash_is_protect_enabled(void)
{
    uint8_t status;
    status = hal_spi_flash_read_status_register(FLASH_CMD_READ_STATUS_REG1);

    if(status & (3<<2))
    {
        return true;
    }
    else
    {
        return false;
    }
}





